#include "adistributedmodule.hh"

ADistributedModule::ADistributedModule(string modulePath, string factorySymbol)
{
    _ready = false;
    _error = false;
    _canFail = false;
    _executed = false;

    _path = modulePath;
    _factory = factorySymbol;
}

ADistributedModule::~ADistributedModule()
{
}

bool ADistributedModule::ExecuteLocal()
{
    bool ret;

    if (false == (ret = executeLocal()))
        _error = true;
    setReady(true);

    return (ret);
}

void ADistributedModule::ExecuteDistributed(bool canFail)
{
    _canFail = canFail;
    start();
}

string ADistributedModule::GetFactorySymbol()
{
    return (_factory);
}

bool ADistributedModule::IsReady()
{
    bool tmp;

    lock();
    tmp = _ready;
    unlock();

    if (tmp == true && _error == true && _canFail == false && _executed == false)
    {
        ExecuteLocal();
        _executed = true;
    }

    return (tmp);
}

bool ADistributedModule::ExecutionFailed()
{
    if (_ready == true)
        return (_error);
    return (false);
}

void ADistributedModule::Stop()
{
    setReady(true);
    if (false == joinThread())
        destroyThread();
}

void ADistributedModule::distribute()
{
    Socket dispatcher = Socket(Socket::TCP4, "127.0.0.1", 2424);
    Protocol dispatcherNetwork;
    Socket server;
    Protocol serverNetwork;
    int serverPort;
    string serverAddress;

    // ############################
    // Connection with dispatcher
    // ############################
    dispatcher.connect();
    dispatcherNetwork.InitClient(&dispatcher);
    if (!dispatcherNetwork.RecvConnectionConfirmation())
    {
        cout << "Error while connecting to the Dispatcher!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    if (!dispatcherNetwork.RecvServerInfo(&serverAddress, &serverPort))
    {
        cout << "Error while receiving Server informations from Dispatcher!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    if (!dispatcherNetwork.sendCode(CLOSE))
    {
        cout << "Error while closing connection to Dispatcher!" << endl;
    }

    // ############################
    // Connection with server
    // ############################
    server = Socket(Socket::TCP4, serverAddress, serverPort);
    server.connect();
    serverNetwork.InitClient(&server);
    if (!serverNetwork.RecvConnectionConfirmation())
    {
        cout << "Error while connecting to the Server!" << endl;
        _error = true;
        setReady(true);
        return;
    }

    // ############################
    // Load module code and data
    // ############################
    char code[1000000];
    int codeSize;
    void *data;
    int dataSize;
    FILE *module;

    if (0 == (module = fopen(_path.c_str(), "r")))
    {
        cout << "Cannot open module file [" << _path.c_str() << "]. Aborting!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    if (0 >= (codeSize = fread(code, 1, 1000000, module)))
    {
        cout << "Cannot read module file [" << _path.c_str() << "]. Aborting!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    if (0 != fclose(module))
        cout << "Cannot close module file [" << _path.c_str() << "]. Aborting!" << endl;
    if (!ExportData(&data, &dataSize))
    {
        cout << "Cannot retrieve module data. Aborting!" << endl;
        _error = true;
        setReady(true);
        return;
    }

    // ############################
    // Send and execute module on server
    // ############################
    serverNetwork.SetModuleName(_factory);
    serverNetwork.SetModuleCode(code, codeSize);
    serverNetwork.SetModuleData(data, dataSize);
    if (!serverNetwork.SendData())
    {
        cout << "Cannot send module to server. Aborting!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    if (!serverNetwork.RecvData())
    {
        cout << "Error while receiving execution result. Aborting!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    if (!serverNetwork.sendCode(CLOSE))
        cout << "Error while closing connection to Server!" << endl;
    serverNetwork.GetModuleData(&data, &dataSize);
    if (!LoadData(data))
    {
        cout << "Cannot load result data in module. Aborting!" << endl;
        _error = true;
        setReady(true);
        return;
    }
    setReady(true);
}

void ADistributedModule::setReady(bool val)
{
    lock();
    _ready = val;
    unlock();
}

void ADistributedModule::start()
{
    if (!(createThread(&distributed_module_starter, (void*)this)))
        cerr << "Distributed module cannot create execution thread!" << endl;
}

void *distributed_module_starter(void *data)
{
    ADistributedModule *classInstance = reinterpret_cast<ADistributedModule*>(data);
    classInstance->distribute();
    return (data);
}
